// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net

package com.x52im.rbchat.logic.chat_root.model;

import com.x52im.rainbowchat.http.logic.dto.TimeToolKit;
import com.x52im.rainbowchat.im.dto.MsgType;
import com.x52im.rainbowchat.im.dto.RevokedMeta;

import net.x52im.mobileimsdk.android.ClientCoreSDK;

import java.io.Serializable;

/**
 * 聊天消息列表中的每个聊天消息数据的封装对象（本对像仅用于各聊天界面中ListView的UI显示时，不会用作别的地方）。
 *
 * @since 1.0
 */
public class Message implements Serializable {

	//======================================================== 核心数据字段 START
	/** 消息发送者的id（"我"发出的消息，则此id就是本地用户的uid） */
	protected String senderId;
	/** 消息发送者的昵称（用于显示） */
	protected String senderDisplayName;
	/** 消息时间戳 */
	protected long date;
	/** 消息内容（注意：此消息内容可能并非扁平文本，可能是对象的JSON字串，以不同消息定义的封装对象为准）。*/
	protected String text;
	/** 消息类型 */
	protected int msgType = MsgType.TYPE_TEXT;
	/** 消息所对应的原始协议包指纹，目前只聊天消息有意义（群聊通知这种系统消息无意义） */
	protected String fingerPrintOfProtocal = null;
	/** 消息所对应的群聊发送者发出的原始包协议包指纹，目前只在收到的消息对象中有意义，且仅用于群聊消息时作为消息"撤回"功能的匹配依据 */
	protected String fingerPrintOfParent = null;
	//======================================================== 核心数据字段 END

	//======================================================== 辅助UI显示字段 START
	/** 消息从网络发送的当前状态. 本字段仅针对发送的消息（而非收到的消息哦） */
	protected int sendStatus = SendStatus.sending;
	
	/** 
	 * 辅助处理状态. 本字段仅针对发送的消息（而非收到的消息哦）.
	 * <p>
	 * 此变量通常用于发送图片、语音留言场合，因为图片文件上传到服务端的过程是
	 * 一个独立的处理过程，需要和文字消息分开处理. */
	protected int sendStatusSecondary = SendStatusSecondary.none;
	/**
	 * 辅助处理状态下的进度值（0~100整数）. 本字段仅针对发送的消息（而非收到的消息哦）.
	 * <p>
	 * 本字段当前仅用于大文件消息的文件数据传输时（当然，你也可以用于其它消息类型，
	 * 这样的值仅辅助UI显示，并非关键数据）。
	 * @since 4.3
	 */
	protected int sendStatusSecondaryProgress = 0;
	
	/**
	 * 辅助下载状态常量.
	 * <p>
	 * 此常量目前仅用于语音留言的声音文件下载时提示下载进度之用（当然以后也可用作它用）. */
	protected DownloadStatus downloadStatus = new DownloadStatus();

	/**
	 * 这是一个特殊的标识：用于记录该条"发出"的消息是"我"转发出去的，而不是正常发出的。
	 * <p>
	 * 本标识目前仅用于：因网络原因发送失败的消息，由用户点失败小图标再次重传时。
	 * <p>
	 * 添加本标识的原因：让此消息在用户点重传时走"转发"逻辑而不是正常的"重传"逻辑，因为存在一种情况：就是"我"转发
	 * 收到的消息（比如大文件消息）失败，如果点重传图标走正常的"重传"逻辑时，会先进行文件的上传完整逻辑完成后再发
	 * 送重传消息，但因此文件本身就是从收到的消息中转发而来，文件自然还没有被下载下来，则此时的重传不可能成功。而
	 * 如果此条消息再走的是"转发"逻辑的话，就不会有问题，因为转发不需要对文件进行上传处理（因为转发的逻辑前提是，
	 * 这条消息本身已是正常发出的消息，文件什么的早以由原发生者上传到服务器了）。
	 *
	 * @since 10.0
	 */
	protected boolean forwardOutgoing = false;

	/**
	 * 是否显示消息气泡上方的时间。
	 * 参照微信的逻辑：http://www.52im.net/thread-3008-1-1.html#40，5分钟内的聊天消息才会在上方显示时间。
	 * <p>
	 * 本参数非持久化变量，仅在消息载入内存时自动计算得出，用于聊天界面中显示顶部的消息时间之用。
	 *
	 * @since 7.1
	 */
	protected transient boolean showTopTime = false;

	/**
	 * 是否高亮显示一次。
	 * <p>
	 * 本参数非持久化变量，仅在由搜索功能进入聊天界面时，用于1次高亮显示该条搜索到消息时使用（高亮动画完成后会重新置为false）。
	 *
	 * @since 8.2
	 */
	protected transient boolean highlightOnce = false;
	//======================================================== 辅助UI显示字段 END

	
	public Message() {
		//
	}

	public Message(String senderId, String name, long date, String text, int isComMsg) {
		this(senderId, name, date, text, isComMsg, SendStatusSecondary.none);
	}
	
	public Message(String senderId, String name, long date, String text, int isComMsg, int sendStatusSecondary) {
		super();

		this.senderId = senderId;
		this.senderDisplayName = name;
		this.date = date;
		this.text = text;
		this.msgType = isComMsg;
		this.sendStatusSecondary = sendStatusSecondary;
	}

	public void setSenderId(String senderId) {
		this.senderId = senderId;
	}

	public String getSenderId() {
		return senderId;
	}

	public String getSenderDisplayName() {
		return senderDisplayName;
	}

	public void setSenderDisplayName(String senderDisplayName) {
		this.senderDisplayName = senderDisplayName;
	}

	public long getDate() {
		return date;
	}
    /**
     * date字段存放的是GMT+0时区的时间戳，UI显示时要转换成人类友好的形式哦.
     *
     * @return 转换成功则返回日期时间字串，否则返回空字符串""
     */
    public String getDateHuman() {
        return TimeToolKit.getTimeStringAutoShort2(TimeToolKit.getDateTime(date), true, true);
    }

	public void setDate(long date) {
		this.date = date;
	}

	/**
	 * 返回消息体（对于文本消息而言，它就是消息字串，而对于复杂的消息而言，本字段值可能是一个对象的JSON序列化字串）。
	 * 
	 * @return
	 */
	public String getText() {
		return text;
	}

	/**
	 * 返回对应消息的消息体对象（有些消息的text里存放的是JSON，本方法就是将JSON转成对应的对象，方便使用）。
	 * 
	 * @return
	 */
	public Object getTextObject() {
		// 文件消息里存放的是FileMeta对象的JSON字串
		if(this.msgType == MsgType.TYPE_FILE) {
			return FileMeta.fromJSON(getText());
		}
        // 短视频消息里存放的是FileMeta对象的JSON字串
		else if(this.msgType == MsgType.TYPE_SHORTVIDEO) {
            return FileMeta.fromJSON(getText());
        }
		// 名片消息里存放的是ContactMeta对象的JSON字串
		else if(this.msgType == MsgType.TYPE_CONTACT) {
			return ContactMeta.fromJSON(getText());
		}
		// 位置消息里存放的是ContactMeta对象的JSON字串
		else if(this.msgType == MsgType.TYPE_LOCATION) {
			return LocationMeta.fromJSON(getText());
		}
		// 被撤回消息里存放的是RevokedMeta对象的JSON字串
		else if(this.msgType == MsgType.TYPE_REVOKE) {
			return RevokedMeta.fromJSON(getText());
		}
		else if(this.msgType == MsgType.TYPE_VOIP$RECORD) {
			return VoipRecordMeta.fromJSON(getText());
		}
		
		return getText();
	}

	public void setText(String text) 
	{
		this.text = text;
	}

	public int getMsgType() 
	{
		return msgType;
	}

	public void setMsgType(int isComMsg) 
	{
		msgType = isComMsg;
	}
	
	public String getFingerPrintOfProtocal()
	{
		return fingerPrintOfProtocal;
	}
	public Message setFingerPrintOfProtocal(String fingerPrintOfProtocal) {
		this.fingerPrintOfProtocal = fingerPrintOfProtocal;
		return this;
	}

	public String getFingerPrintOfParent() {
		return fingerPrintOfParent;
	}
	public Message setFingerPrintOfParent(String fingerPrintOfParent) {
		this.fingerPrintOfParent = fingerPrintOfParent;
		return this;
	}

	public int getSendStatus() {
		return sendStatus;
	}
	public void setSendStatus(int sendStatus) {
		this.sendStatus = sendStatus;
	}
	
	public int getSendStatusSecondary() {
		return sendStatusSecondary;
	}
	public void setSendStatusSecondary(int sendStatusSecondary) {
		this.sendStatusSecondary = sendStatusSecondary;
	}

	public DownloadStatus getDownloadStatus() {
		return downloadStatus;
	}

	public int getSendStatusSecondaryProgress() {
		return sendStatusSecondaryProgress;
	}
	public void setSendStatusSecondaryProgress(int sendStatusSecondaryProgress) {
		this.sendStatusSecondaryProgress = sendStatusSecondaryProgress;
	}

	public boolean isShowTopTime() {
		return showTopTime;
	}
	public void setShowTopTime(boolean showTopTime) {
		this.showTopTime = showTopTime;
	}

	/** 是否"我"发出的消息 */
	public boolean isOutgoing() {
		if(this.senderId != null && this.senderId.equals(ClientCoreSDK.getInstance().getCurrentLoginUserId()))
			return true;
		else
			return false;
    }

	/**
	 * 是否是控制类消息（这主要用于区分普通的聊天消息，用于聊天界面中判定是否可以删除、撤回逻辑时）。
	 *
	 * @return true表示是，否则不是
	 */
	public boolean isControl() {
		return this.msgType == MsgType.TYPE_SYSTEAM$INFO || this.msgType == MsgType.TYPE_REVOKE;
	}

	/**
	 * 是否是被允许撤回的消息。
	 *
	 * @return true表示是，否则不是
	 */
	public boolean isRevokeEnabled() {
		// 必须是发出的消息且正常发送完成的消息才允许被撤回
		return !isControl() && isOutgoing() && this.sendStatus == SendStatus.beReceived;
	}

	/**
	 * 是否是被允许转发的消息。
	 *
	 * @return true表示是，否则不是
	 */
	public boolean isForwardEnabled() {
		return !isControl() && this.msgType != MsgType.TYPE_VOIP$RECORD
				// 发出的消息时，必须是正常发送完成的消息才允许转发
				&& ((this.sendStatus == SendStatus.beReceived && isOutgoing()) || !isOutgoing());
	}

	public boolean isHighlightOnce() {
		return highlightOnce;
	}

	public void setHighlightOnce(boolean highlightOnce) {
		this.highlightOnce = highlightOnce;
	}

	@Override
    public String toString(){
		return "[senderId="+senderId+", senderDisplayName="+senderDisplayName+", date="+date+", text="+text
				+", msgType="+msgType+", fingerPrintOfProtocal="+fingerPrintOfProtocal+", fingerPrintOfParent="+fingerPrintOfParent
				+", highlightOnce="+highlightOnce+", forwardOutgoing"+forwardOutgoing+"]";
	}

	public boolean isForwardOutgoing() {
		return forwardOutgoing;
	}

	public void setForwardOutgoing(boolean forwardOutgoing) {
		this.forwardOutgoing = forwardOutgoing;
	}
	
	/**
	 * 文字消息的发送状态常量.
	 */
	public interface SendStatus {
		/** 消息发送中 */
		int sending = 0;
		/** 消息已被对方收到（我方已收到QoS应答包） */
		int beReceived = 1;
		/** 消息发送失败（在QoS超时重传的时间内未收到应答包） */
		int sendFaild = 2;
	}
	
	/**
	 * 辅助发送状态常量.
	 * <p>
	 * 此常量通常用于发送图片、语音留言场合，因为图片文件上传到服务端的过程是
	 * 一个独立的处理过程，需要和文字消息分开处理.
	 */
	public interface SendStatusSecondary {
		/** 无需处理 */
		int none = 0;
		/** 等待处理 */
		int pending = 1;
		/** 处理中 */
		int processing = 2;
		/** 成功处理完成 */
		int processOk = 3;
		/** 处理失败 */
		int processFaild = 4;
	}
	
	/**
	 * 辅助下载状态常量.
	 * <p>
	 * 此常量目前语音留言的声音文件下载时提示下载进度之用.
	 */
	public static class DownloadStatus implements Serializable {
		/** 无需处理 */
		public final static int NONE = 0;
		/** 处理中 */
		public final static int PROCESSING = 1;
		/** 成功处理完成 */
		public final static int PROCESS_OK = 2;
		/** 处理失败 */
		public final static int PROCESS_FAILD = 3;
		
		private int status = NONE;
		private int progress = 0;
		
		public int getStatus()
		{
			return status;
		}
		public void setStatus(int status)
		{
			this.status = status;
		}
		public int getProgress()
		{
			return progress;
		}
		public void setProgress(int progress)
		{
			this.progress = progress;
		}
	}
	
	/**
	 * 辅助发送状态的处理结果观察者统一接口.
	 */
	public interface SendStatusSecondaryResult {
		/** 状态变迁到“处理中”状态时要调用的方法 */
		void processing();
		/** 状态变迁到“处理失败”时要调用的方法 */
		void processFaild();
		/** 状态变迁到“处理成功”时要调用的方法 */
		void processOk();
	}

	//------------------------------------------------------------------------------------------- 内部类 END
}
